<?php

/**
 * Тут находятся константы с кодами ошибок
 * TODO: файл должен быть полностью переписан
 */

define ('ERROR_NEED_MORE_DATA',            101);
define ('ERROR_CONNECT_FAILED',            102);
define ('BAD_QUERY',                       103);
define ('CREATE_DATABASE_ERROR',           104);
define ('ERROR_DISCONNECT_IF_NOT_CONNECT', 105);
define ('ERROR_DISCONNECT_FAILED',         106);
define ('SELECT_DATABASE_ERROR',           107);
define ('INVALID_PORT',                    108);
define ('DONT_EXECUTE_QUERY',              109);
define ('DELETE_DATABASE_ERROR',           110);
define ('NOT_CONNECTION',                  111);
define ('GET_NUMROWS_ERROR',               112);
define ('MYSQL_EXT_NOT_INSTALLED',         113);
define ('FETCH_ARRAY_ERROR',               114);


class mysql
{

// Объект (для Singleton)
static private $instance = null;
// Инициализируются переменные, необходимые для корректной работы
public $lastId;
public $connectionId;
public $resultQuery;
private $lastSQL;
private $errorCount;
private $queryResult;

private function __construct($host = '', $port = 0, $name = '', $password = '', $database = '')
{
    if (!empty($host) && $port != 0 && !empty($name)) {
        $this->connect($host, $port, $name, $password);
    }
    if (!empty($database)) {
        $this->setDatabase($database);
    }
}

function connect($host = 'localhost', $port = 3306, $name = 'root', $pass = '')
{
    if (!function_exists('mysql_connect')) {
        return $this->SetError(MYSQL_EXT_NOT_INSTALLED);
    }
    if(empty($host) || empty($name)) {
        $this->SetError(ERROR_NEED_MORE_DATA);
        return false;
    }
    if(!is_numeric($port) || $port < 1 || $port > 65535) {
        $this >SetError(INVALID_PORT);
        return false;
    }
    $connectId = @mysql_connect($host . ':' . $port, $name, $pass);
    if (!$connectId) {
        $this->SetError(ERROR_CONNECT_FAILED);
        return false;
    }
    $this->connectionId = $connectId;
    return true;
}

function isConnected()
{
    if (!isset($this->connectionId) || (gettype($this->connectionId) != "resource")) {
        return false;
    } else {
        return true;
    }
}

function disconnect()
{
    if (!$this->IsConnected()) {
        $this->SetError(NOT_CONNECTION);
        return false;
    }
    $disconnectQuery = @mysql_close($this->connectionId);
    if (!$disconnectQuery) {
        $this->SetError(ERROR_DISCONNECT_FAILED);
        return false;			
    } else {
        return true;
    }
}

function setDatabase($name)
{
    if (!$this->IsConnected()) {
        $this->SetError(NOT_CONNECTION);
        return false;
    }
    $selectdbQuery = @mysql_select_db($name);
    if (!$selectdbQuery) {
        $this->SetError(SELECT_DATABASE_ERROR);
        return false;
    } else {
        $this->currentDatabase = $name;
        return true;
    }
}

function createDatabase($name, $if_not_exists = true, $setAsCurrent = false)
{
    if (!$this->IsConnected()) {
        $this->SetError(NOT_CONNECTION);
        return false;
    }
    if ($if_not_exists) {
        $prepareQuery = "CREATE DATABASE IF NOT EXISTS `" . $name . "`";
    } else {
        $prepareQuery = "CREATE DATABASE `" . $name . "`";
    }
    if (!$this->Query($prepareQuery)) {
        $this->SetError(CREATE_DATABASE_ERROR);
        return false;
    }
    if ($setAsCurrent) {
        $this->SelectDatabase($name);
    }
    return true;
}

function getCurrentDatabase()
{
    if (isset($this->currentDatabase)) {
        return $this->currentDatabase;
    } else {
        return NULL;
    }
}

function dropDatabase($db, $if_exists = false)
{
    if (!$this->IsConnected()) {
        $this->SetError(NOT_CONNECTION);
        return false;
    }
    if (!$if_exists) {
        $preparedQuery = "DROP DATABASE `" . $db . "`";
    } else {
        $preparedQuery = "DROP DATABASE IF EXISTS `" . $db . "`";
    }
    if (!$this->Query($preparedQuery)) {
        $this->SetError(DELETE_DATABASE_ERROR);
        return false;
    } else {
        return true;
    }
}


function query($query, $connectionID = '')
{
    if (!$this->IsConnected()) {
        $this->SetError(NOT_CONNECTION);
        return false;
    }
    $this->lastSQL = $query;
    if (empty($connectionID)) {
        $queryResult = mysql_query($query);
    } else {
        $queryResult = mysql_query($query, $connectionID);
    }
    if ($queryResult) {
        $this->queryResult = $queryResult;
        return $queryResult;
    } else {
        $this->SetError(BAD_QUERY);
        return false;
    }
}


function clearQueryResult()
{
    if (isset($this->resultQuery)) {
        unset($this->resultQuery);
    }
}

	
function clearError()
{
    if (isset($this->lastError)) {
        unset($this->lastError);
    }
    if (isset($this->errorCount)) {
        unset($this->errorCount);
    }
}

	
function setError($code = 0)
{
    $this->lastError = $code;
    $this->errorCount++;
}


function getErrorCount()
{
    if (isset($this->errorCount)) {
        return (int)$this->errorCount;
    } else {
        return 0;
    }
}

	
function getLastSQL()
{
    if (isset($this->lastSQL)) {
        return $this->lastSQL;
    } else {
        return NULL;
    }
}

	
function getError($level = 'class')
{
    if ($level == 'class') {
        if (!isset($this->lastError)) {
            return NULL;
        } else {
            $errorArray = array(
            ERROR_NEED_MORE_DATA    =>  'Указаны не все данные необходимые для подключения.',
            ERROR_CONNECT_FAILED    =>  'Не удалось соедениться с MySQL базой данных.',
            BAD_QUERY               =>  'Не удалось выполнить MySQL запрос.',
            CREATE_DATABASE_ERROR   =>  'Не удалось создать базу данных.',
            ERROR_DISCONNECT_FAILED =>  'Не удалось закрыть MySQL соединение.',
            SELECT_DATABASE_ERROR	=>  'Не удалось выбрать указанную базу данных.',
            INVALID_PORT			=>  'Неправильно указанный порт.',
            DELETE_DATABASE_ERROR	=>  'Возникла проблема при удалении базы данных.',
            NOT_CONNECTION          =>  'Не установлено соединение с базой данных.',
            GET_NUMROWS_ERROR       =>  'Не удалось получить количество элементов.',
            MYSQL_EXT_NOT_INSTALLED =>  'Расширение MySQL не найдено.',
            FETCH_ARRAY_ERROR       =>  'Произоша ошибка при обработке результата пред. запроса.',
            DONT_EXECUTE_QUERY      =>  'Не был выполнен запрос (или выполнен с ошибками).');
            return $errorArray[$this->lastError];
        }
    } elseif ($level == 'mysql') {
        $error = mysql_error();
        if (!empty($error)) {
            return $error;
        } else {
            return NULL;
        }
    } else {
        return NULL;
    }
}


function numRows()
{
    $result = @mysql_num_rows($this->queryResult);
    if (!$result) {
        $this->SetError(GET_NUMROWS_ERROR);
        return false;
    } else {
        return $result;
    }
}


function fetch($queryResult = null, $resultType = MYSQL_BOTH)
{
    if (!$this->IsConnected()) {
        $this->SetError(NOT_CONNECTION);
        return false;
    } elseif (!isset($this->queryResult)) {
        $this->SetError(DONT_EXECUTE_QUERY);
        return false;
    } else {
        if (is_null($queryResult)) {
            $prepareResult = @mysql_fetch_array($this->queryResult, $resultType);
        } else {
            $prepareResult = @mysql_fetch_array($queryResult, $resultType);
        }
        if ($prepareResult) {
            return $prepareResult;
        } else {
            $this->SetError(FETCH_ARRAY_ERROR);
            return false;
        }
    }
}


function affectedRows()
{
    if (!$this->IsConnected()) {
        $this->SetError(NOT_CONNECTION);
        return false;
    } elseif (!isset($this->queryResult)) {
        $this->SetError(DONT_EXECUTE_QUERY);
        return false;
    } else {
        return mysql_affected_rows($this->connectionId);
    }
}

	
function insert($table, $valuesArray, $connectionID = null)
{
    if (! $this->IsConnected()) {
        $this->SetError(NOT_CONNECTION);
        return false;
    }
    if (! is_array($valuesArray)) {
        $this->SetError(INVALID_INSERT_QUERY);
        return false;
    }
    foreach ($valuesArray as $queryCreator) {
        if (! is_numeric($queryCreator)) {
            $queryCreator = "'" . $queryCreator . "'";
        }
        $quotedQuery[] = $queryCreator;
    }
    $valuesArray = implode(', ', $quotedQuery);
    if (! is_null($connectionID)) {
        $queryResult = $this->query("INSERT INTO `" . $table . "` VALUES (" . $valuesArray . ")", $connectionID);
    } else {
        $queryResult = $this->query("INSERT INTO `" . $table . "` VALUES (" . $valuesArray . ")");
    }
    if (!$queryResult) {
        $this->SetError(BAD_QUERY);
        return false;
    } else {
        $this->lastId = mysql_insert_id();
        return $this->lastId;
    }
}

function select($table, $selectArray = '*', $limit = null, $whereConditions = null)
{
    $keys = '';
    if (is_array($selectArray)) {
        foreach ($selectArray as $key) {
            if (!is_numeric($key)) {
                $key = "`" . $key . "`";
            }
            if (strlen($keys) == 0) {
                $keys .= $key;
            } 
            else {
                $keys .= ', ' . $key;
            }
        }
    }
    elseif (is_string($selectArray)) {
        $keys = $selectArray;
    } else {
        $keys = '*';
    }
    
    if (! is_null($limit)) {
        if (is_numeric($limit)) {
            $limit = ' LIMIT ' . $limit;
        } else {
            $limit = '';
        }
    } else {
        $limit = '';
    }
    return $this->query('SELECT ' . $keys . ' FROM `' . $table . '`' . $this->buildWhereClause($whereConditions) . $limit);
    
}


function buildWhereClause($whereArray = null)
{
    $whereQuery = ''; // Инициализация переменной. Необходима для корректной работы в PHP 5.3.0
    if (is_array($whereArray)) {
        foreach ($whereArray as $key => $value) {
            if (!is_numeric($value)) {
                $value = "'" . $value . "'";
            }
            if (strlen($whereQuery) == 0) {
                $whereQuery .= "`" . $key . "` = " . $value;
            } else {
                $whereQuery .= " AND `" . $key . "` = " .  $value;
            }
        }
        $whereQuery = ' WHERE ' . $whereQuery;
    } elseif (is_string($whereArray)) {
        $whereQuery = ' WHERE ' . $whereArray;
    } else {
        $whereQuery = '';
    }
    return $whereQuery;
}

function buildOrClause($row, $array)
{
    $orQuery = '';
    foreach($array as $element) {
        if (! is_numeric($element)) {
            $element = "'" . $element . "'";
        }
        if (strlen($orQuery) == 0) {
            $orQuery .= "`" . $row . "` = " . $element;
        } else {
            $orQuery .= " OR `" . $row . "` = " .  $element;
        }
    }
    return $orQuery;
}

function update($table, $updateList, $whereConditions)
{
    $updateQuery = ''; // Инициализация переменной. Необходима для корректной работы в PHP 5.3.0
    foreach ($updateList as $key => $newValue) {
        if (!is_numeric($newValue)) {
            $newValue = "'" . $newValue . "'";
        }
        if (strlen($updateQuery) == 0) {
            $updateQuery .= '`' . $key . '` = ' .  $newValue;
        } else {
            $updateQuery .= ', `' . $key . '` = ' .  $newValue;
        }
    }
    return $this->query('UPDATE ' . $table . ' SET ' . $updateQuery . $this->buildWhereClause($whereConditions));
}

static function getInstance($host = '', $port = 0, $name = '', $password = '', $database = '') {
    if (self::$instance == null) {
        self::$instance = new mysql($host, $port, $name, $password);
    }
    return self::$instance;
}

function lastId() {
    return mysql_insert_id();
}

}